from OpenGL.GL import *
from OpenGL.GLUT import *
from OpenGL.GLU import *
from trackball import Trackball
from numpy import *


class GlWindow:
    def __init__(self, init_func, display_func, key_func=None, title="OpenGL", idle_render=False):
        self.display_func = display_func
        self.key_func = key_func
        self.current_size = 200, 200
        self.tb = Trackball()
        self.change = True
        self.wheel_change = 0
        
        glutInit(sys.argv)
        glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH)
        glutInitWindowSize(200, 200)
        glutInitWindowPosition(100, 100)
        window = glutCreateWindow(title)
        glutDisplayFunc(self.display)
        glutReshapeFunc(self.resize)
        glutKeyboardFunc(self.keyPressed)
        glutSpecialFunc(self.keyPressed)
        glutMouseFunc(self.mouse_click)
        glutMotionFunc(self.mouse_move)
        
        try:
            glutMouseWheelFunc(self.mouse_wheel)
        except:
            pass
            
        if idle_render:
            glutIdleFunc(self.display)
        init_func()
        
    def start(self):
        glutMainLoop()

    def resize(self, width, height):
        self.change = True
        self.current_size = width, height

    def display(self):
        try:
            w, h = self.current_size
            mtx = self.tb.get_matrix()
            self.display_func(w, h, mtx, self.wheel_change, self.change)
            glutSwapBuffers()
            self.change = False
            self.wheel_change = 0
        except:
            from traceback import print_exc
            print_exc()
            from os import _exit
            _exit(0)

    def keyPressed(self, *args):
        self.change = True
        if args[0] == '\033':
            print 'Closing..'
            # destroy_PBOs()
            # destroy_texture()
            exit()
        
        self.key_func(args[0])

    def mouse_click(self, button, state, x, y):
        if state == GLUT_UP:
            self.tb.mouse_release()
        elif button == GLUT_LEFT_BUTTON:
            self.change = True
            w, h = self.current_size
            x = (float(x) / w) * 2 - 1
            y = 1 - (float(y) / h) * 2
            self.tb.mouse_click(x, y)
        elif button == 3: # wheel up
            self.change = True
            self.wheel_change = +1
        elif button == 4: # wheel down
            self.change = True
            self.wheel_change = -1

    def mouse_wheel(self, wheel, direction, x, y):
        self.change = True
        self.wheel_change = direction

    def mouse_move(self, x, y):
        if self.tb.mouse: self.change = True
        w, h = self.current_size
        x = (float(x) / w) * 2 - 1
        y = 1 - (float(y) / h) * 2
        self.tb.mouse_move(x, y)
        


def init_gl():
    width, height = 800, 800
    glClearColor(0.1, 0.1, 0.5, 1.0)
    glDisable(GL_DEPTH_TEST)
    glViewport(0, 0, width, height)
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(60.0, width/float(height), 0.1, 10.0)
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
    glEnable(GL_LIGHT0)
    red   = ( 1.0, 0.1, 0.1, 1.0 )
    white = ( 1.0, 1.0, 1.0, 1.0 )
    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE,  red  )
    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, white)
    glMaterialf( GL_FRONT_AND_BACK, GL_SHININESS, 60.0)
    glEnable(GL_LIGHTING)
    glEnable(GL_DEPTH_TEST)
    glDepthFunc(GL_LESS)


def render_scene(width, height, matrix, wheel, change):
    glViewport(0, 0, width, height)
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    gluPerspective(60.0, width/float(height), 0.1, 10.0)

    glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)    
    glMatrixMode(GL_MODELVIEW)
    m = eye(4, dtype=float64)
    zoom = 0.1 + wheel / 25.0
    m[0:3, 0:3] = matrix * zoom
    m[0:3, 3] = array([0, 0, -3])
    glLoadMatrixd(m.transpose())
    
    glutSolidTeapot(1.0)
    return True


if __name__ == "__main__":
    window = GlWindow(init_gl, render_scene)
    window.start()

